package kmeans.bean;


import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import kmeans.exception.WXKmeansException;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class WXKmeansCentroids {
    private static final Logger LOG = LogManager.getLogger(WXKmeansCentroids.class);
    //存取的样子是聚类的类别数=[中心点的三个维度的值] 如：1=[1,2,3]
    private Map<Integer, ArrayList<Double>> centroids = new HashMap<>();
    private int dimension=3;
    private String centroidPath;


    public WXKmeansCentroids(int dimension, String centroidPath) {
        this.dimension = dimension;
        initCentroids(centroidPath);
    }

    public WXKmeansCentroids(String centroidPath) {
        initCentroids(centroidPath);
    }


    private void initCentroids(String path) {

        try {
            Configuration configuration = new Configuration();
            FileSystem fileSystem = FileSystem.get(URI.create(path), configuration);
            FSDataInputStream inputStream = null;
            System.out.println("Start read centroids file, URI: " + path);
            inputStream = fileSystem.open(new Path(path));
            BufferedReader d = new BufferedReader(new InputStreamReader(inputStream));
            String line = null;
            while ((line = d.readLine()) != null) {
                line = line.replace("\t", " ").trim();
                    System.out.println(line);
                String[] data = line.split(" ");
                if (data.length != dimension + 1) {
                    throw new WXKmeansException("Centroid Dimension Error");
                }
                int clusterid = Integer.valueOf(data[0]);
                ArrayList<Double> centroid = new ArrayList<>();
                for (int i = 1; i < dimension + 1; i++) {
                    centroid.add(Double.valueOf(data[i]));
                }
                centroids.put(clusterid, centroid);
            }
            System.out.println("初始中心点文件成功读取------Centroid 值为: \n" + toString());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (WXKmeansException e) {
            e.printStackTrace();
        }
    }

    public int getClusterid(WXKmeansData data) throws WXKmeansException {
        double distance = Double.MAX_VALUE;
        ArrayList<Double> valueslist = new ArrayList<>();
        String[] values = String.valueOf(data.getkMeansText()).split(" ");
        for (String s : values) {
            valueslist.add(Double.parseDouble(s));
        }
        int clusterid = -1;
        for (Integer i : this.getCentroids().keySet()) {
            double temp = getdistance(centroids.get(i), valueslist);
            if (temp < distance) {
                distance = temp;
                clusterid = i;
            }
        }
        return clusterid;
    }

    public static double getdistance(ArrayList<Double> centroid, ArrayList<Double> data) {
        double distance = 0;
        try {
            if (centroid.size() != data.size()) {
                throw new WXKmeansException("维度出错了");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        for (int i = 0; i < centroid.size(); i++) {
            distance += Math.pow(centroid.get(i)-data.get(i),2);
        }
        distance = Math.sqrt(distance);
        System.out.println(distance);
        return distance;

    }

    public Map<Integer, ArrayList<Double>> getCentroids() {
        return centroids;
    }




    @Override
    public String toString() {
        StringBuffer sb = new StringBuffer();

        for (Integer i : centroids.keySet()) {
            sb.append(i + "\t");
            for (Double j : centroids.get(i)) {
                sb.append(j + "\t ");
            }
            sb.append("\n");
        }

        return sb.toString();
    }

    public boolean isEquals(WXKmeansCentroids o, Double error) {
        boolean flag = true;
        for (Integer i : centroids.keySet()) {
            if (!arrayEquals(centroids.get(i), o.getCentroids().get(i), error)) {
                flag = false;
                break;
            }
        }
        return flag;
    }

    private boolean arrayEquals(ArrayList<Double> a, ArrayList<Double> b, Double error) {
        boolean flag = true;

        for (int i = 0; i < dimension; i++) {
            if (Math.abs(a.get(i) - b.get(i)) > error) {
                flag = false;
                break;
            }
        }
        return flag;
    }
}
